﻿using ChromeTabs;
using GalaSoft.MvvmLight.Messaging;
using JiveMessenger.Misc;
using JiveMessenger.Models;
using JiveMessenger.NotifyWindow;
using JiveMessenger.User;
using JiveMessenger.Utilities;
using JiveMessenger.ViewModels;
using JmShared;
using Microsoft.Win32;
using NLog;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shell;
using System.Windows.Threading;
using WpfColorFontDialog;
namespace JiveMessenger.Views
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private DispatcherTimer _updateTimer;
        private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
        private bool IsFlashing { get; set; }
        private NotifyMessageManager NotifyMessageMgr { get; set; }
        private AdornerLayer adornerLayer;
        public MainWindow()
        {
            if (Properties.Settings.Default.Left == 0 && Properties.Settings.Default.Top == 0 && ProgramSettings.Instance.JustUpdated)
                this.WindowStartupLocation = System.Windows.WindowStartupLocation.CenterScreen;

            InitializeComponent();
            if (DesignerProperties.GetIsInDesignMode(this))
                return;

            this.NotificationsBarBorder.Height = 0;
            InitStartup();
            LoadWindowState();
            InitEvents();
            StartUpdateCheckTimer();
            StartUpdateChecker(false);
            SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged;
           
            //CreateJumpList();
        }

        void SystemEvents_PowerModeChanged(object sender, PowerModeChangedEventArgs e)
        {
            switch (e.Mode)
            {
                case PowerModes.Suspend:
                    VmHelper.Locator.CurrentClient.Connection.Logout();
                    break;
                case PowerModes.Resume:

                    break;
            }
        }

        #region initialization

        public void StartUpdateCheckTimer()
        {
            _updateTimer = new DispatcherTimer();
            _updateTimer.Interval = TimeSpan.FromHours(3);
            _updateTimer.Tick += (s, args) =>
                {
                    StartUpdateChecker(false);
                };
            _updateTimer.Start();
        }

        /// <summary>
        /// Async check for program updates. Adds a notification if a new version are found.
        /// </summary>
        private void StartUpdateChecker(bool isManualCheck)
        {
            Task t = Task.Factory.StartNew(() =>
            {
                try
                {
                    int currVersion;
                    string currVersionString = Assembly.GetExecutingAssembly().GetName().Version.ToString();
                    if (!Int32.TryParse(currVersionString.Replace(".", ""), out currVersion))
                        return;
                    Updater up = Updater.GetLatestVersion();
                    int latestVersion;
                    if (!Int32.TryParse(up.VersionNumber.Replace(".", ""), out latestVersion))
                        return;

                    if (latestVersion > currVersion && up.VersionNumber != Properties.Settings.Default.SkipUpdateVersion)
                    {
                        // WriteStatusLog(string.Format("New version of Autohaxxer found: {0}. Your current version is {1}.", up.VersionNumber, currVersionString), Color.Black);
                        Messenger.Default.Send(new NotificationMessage<Updater>(this, up, "NewApplicationVersionFound"));
                        if (isManualCheck)
                        {
                            this.Dispatcher.BeginInvoke(() =>
                                {
                                    MessageBoxResult result = MessageBox.Show(this, string.Format("A new version of Jive Messenger is available. Your current version is {0}, and the new version is {1}. Do you wish to update?", Assembly.GetExecutingAssembly().GetName().Version.ToString(), up.VersionNumber), "Update", MessageBoxButton.YesNo, MessageBoxImage.Question);

                                    if (result == MessageBoxResult.OK || result == MessageBoxResult.Yes)
                                        VmHelper.Locator.CurrentClient.DownloadApplicationUpdate(up.DownloadUrl);
                                });

                        }
                    }
                    else
                    {
                        if (isManualCheck)
                            this.Dispatcher.BeginInvoke(() => MessageBox.Show(this, string.Format("You already have the most recent version of Jive Messenger")));
                    }
                }
                catch (Exception e)
                {
                    //   WriteStatusLog("Error during update: " + e.Message, Color.Red);
                    Logger.ErrorException("Error during update", e);
                }
            });
        }
        /// <summary>
        /// Initialize eventhandlers
        /// </summary>
        private void InitEvents()
        {
            AppDomain currentDomain = AppDomain.CurrentDomain;
            currentDomain.UnhandledException += new UnhandledExceptionEventHandler(HandleUnhandledExceptions);
        }

        private void InitStartup()
        {
            if (!Directory.Exists(ProgramSettings.Instance.DataFolder))
            {
                Directory.CreateDirectory(ProgramSettings.Instance.DataFolder);
            }
            this.NotifyMessageMgr = new NotifyMessageManager(Screen.Width, Screen.Height, 200, 150);
            this.NotifyMessageMgr.Start();

            Messenger.Default.Register<NotifyMessage>(this, msg =>
            {
                this.NotifyMessageMgr.EnqueueMessage(msg);
            });


            Messenger.Default.Register<string>(this, ShowMessage);

            Messenger.Default.Register<NotificationMessage<string>>(this, msg =>
                {
                    switch (msg.Notification)
                    {
                        case "OpenUpdateProgressWindow":
                            UpdateDownloadProgress progressWin = new UpdateDownloadProgress(msg.Content);
                            progressWin.WindowStartupLocation = System.Windows.WindowStartupLocation.CenterOwner;
                            progressWin.Owner = this;
                            progressWin.ShowDialog();
                            break;
                    }
                });


            Messenger.Default.Register<NotificationMessage<ViewModelChat>>(this, msg =>
                {
                    switch (msg.Notification)
                    {
                        case "OpenChatWindow":
                            OpenChatWindow(msg.Content);
                            break;
                        case "OpenChatTabInWindow":
                            {
                                ChatWindow win = OpenChatWindow(msg.Content);
                                win.DoWhenLoaded(chatWin =>
                                    {
                                        StartWindowDrag(chatWin);
                                    });
                                break;
                            }
                    }
                });



            Messenger.Default.Register<NotificationMessage<double>>(this, (msg) =>
                {
                    switch (msg.Notification)
                    {

                        case "SetWindowWidth":
                            this.Dispatcher.BeginInvoke(() =>
                                {
                                    this.Width = msg.Content;
                                });
                            break;
                        case "SetWindowHeight":
                            this.Dispatcher.BeginInvoke(() =>
                                {
                                    this.Height = msg.Content;
                                });
                            break;
                    }
                });

            Messenger.Default.Register<NotificationMessage<Uri>>(this, msg =>
                {
                    switch (msg.Notification)
                    {
                        case "OpenUrl":
                            Process.Start(new ProcessStartInfo(msg.Content.AbsoluteUri));
                            break;
                    }
                });

            Messenger.Default.Register<NotificationMessage>(this, msg =>
                {
                    var viewModelLocator = (ViewModelLocator)Application.Current.Resources["Locator"];
                    switch (msg.Notification)
                    {
                        case "OpenLogs":
                            if (msg.Sender is ViewModelChatWindow)
                            {
                                Messenger.Default.Send(new NotificationMessageAction<ChatWindow>(msg.Sender, "GetChatWindow", reply =>
                                {
                                    OpenLog(reply);
                                }));
                            }
                            else
                                OpenLog(this);
                            break;
                        case "Nudge":
                            if (this.IsActive)
                            {
                                Storyboard board = this.Resources["ShakeWindowAnim"] as Storyboard;
                                if (board != null)
                                    board.Begin();
                            }
                            break;
                        case "OpenContactListWindow":
                            OpenContactListWindow();
                            break;
                        case "OpenRegisterWindow":
                            OpenRegisterWindow();
                            break;
                        case "OpenResetPasswordWindow":
                            OpenResetPasswordWindow();
                            break;
                        case "AddUserEmoticons":
                            AddUserEmoticons();
                            break;
                        case "AddEmoticon":
                            ChatWindow window = null;
                            if (msg.Sender is ViewModelTab)
                            {
                                window = GetChatWindow(((ViewModelTab)msg.Sender).ID);
                            }
                            //if (msg.Sender == this.DataContext || msg.Sender.Equals(((ViewModelMainWindow)this.DataContext).CurrentActiveViewModel))
                            OpenAddEmoticonWindow(window);
                            break;
                        case "ClosePmWindows":
                            foreach (ViewModelChatWindow vmp in viewModelLocator.CurrentClient.OpenWindowViewModels.Values)
                                Messenger.Default.Send(new NotificationMessage(vmp, "CloseWindow"));
                            break;
                        case "FlashMainWindow":
                            if (!IsFlashing)
                            {
                                this.Dispatcher.BeginInvoke(() =>
                                    {
                                        this.FlashWindow();
                                        IsFlashing = true;
                                    });
                            }
                            break;
                        case "OpenAddContacts":
                            OpenAddContacts(msg.Target as string);
                            break;
                        case "OpenCreateGroup":
                            OpenCreateGroups();
                            break;
                        case "OpenOptions":
                            OptionsPage page = OptionsPage.Personal;
                            if (msg.Target != null && msg.Target.GetType() == typeof(OptionsPage))
                                page = (OptionsPage)msg.Target;
                            if (msg.Sender == this.DataContext)
                                OpenOptions(this, page);
                            else if (msg.Sender is ViewModelChatWindow)
                            {
                                Messenger.Default.Send(new NotificationMessageAction<ChatWindow>(msg.Sender, "GetChatWindow", reply =>
                                            {
                                                OpenOptions(reply, page);
                                            }));
                            }
                            else
                                OpenOptions(this, page);
                            break;
                        case "OpenNotifications":
                            OpenNotifications();
                            break;
                        case "ChangeFont":
                            {
                                Window ownerWin = null;
                                if (msg.Sender is ViewModelChatWindow)
                                    Messenger.Default.Send(new NotificationMessageAction<ChatWindow>(msg.Sender, "GetChatWindow", reply =>
                                    {
                                        ownerWin = reply;
                                    }));
                                else if (msg.Sender.GetType() == typeof(PersonalPage))
                                {
                                    PersonalPage pp = msg.Sender as PersonalPage;
                                    ownerWin = Window.GetWindow(pp);
                                }
                                if (ownerWin == null)
                                    ownerWin = this;

                                EditFont(ownerWin);
                                break;
                            }
                        case "StartUpdateCheck":
                            StartUpdateChecker(true);
                            break;
                        case "OpenAboutWindow":
                            {
                                Window ownerWin = null;
                                if (msg.Sender is ViewModelChatWindow)
                                    Messenger.Default.Send(new NotificationMessageAction<ChatWindow>(msg.Sender, "GetChatWindow", reply =>
                                    {
                                        ownerWin = reply;
                                    }));
                                AboutBox about = new AboutBox(ownerWin);
                                about.ShowDialog();
                                break;
                            }
                        case "SaveWindowState":
                            {
                                this.Dispatcher.BeginInvoke(() =>
                                {
                                    SaveWindowState();
                                });
                                break;
                            }
                    }
                });


            Messenger.Default.Register<NotificationMessageAction<MessageBoxResult>>(this, msg =>
            {
                switch (msg.Notification)
                {
                    case "QueryDeleteGroup":
                        {
                            var result = MessageBox.Show(msg.Target.ToString(), "Delete group?", MessageBoxButton.OKCancel, MessageBoxImage.Warning);
                            msg.Execute(result);
                            break;
                        }
                    case "QueryDeleteFile":
                        {
                            var result = MessageBox.Show(msg.Target.ToString(), "Delete file?", MessageBoxButton.OKCancel, MessageBoxImage.Warning);
                            msg.Execute(result);
                            break;
                        }
                    case "QueryRemoveGroupMember":
                        {
                            var result = MessageBox.Show(msg.Target.ToString(), "Remove group member?", MessageBoxButton.OKCancel, MessageBoxImage.Warning);
                            msg.Execute(result);
                            break;
                        }
                    case "QueryDownloadUpdate":
                        {
                            var result = MessageBox.Show(msg.Target.ToString(), "Download update?", MessageBoxButton.OKCancel, MessageBoxImage.Question);
                            msg.Execute(result);
                            break;
                        }
                }

            });

            Messenger.Default.Register<NotificationMessage<IEmoticon>>(this, msg =>
            {
                switch (msg.Notification)
                {
                    case "AddEmoticon":
                        {
                            ChatWindow window = null;
                            if (msg.Sender is ViewModelChat)
                            {
                                window = GetChatWindow(((ViewModelChat)msg.Sender).ID);
                            }
                            OpenAddEmoticonWindow(window, msg.Content);
                            break;
                        }
                    case "EditEmoticon":
                        {
                            ChatWindow window = null;
                            if (msg.Sender is ViewModelChat)
                            {
                                window = GetChatWindow(((ViewModelChat)msg.Sender).ID);
                            }
                            EditEmoticon(window, msg.Content);
                            break;
                        }
                }

            });

            Messenger.Default.Register<NotificationMessage<IChatGroup>>(this, msg =>
                {
                    if (msg.Notification == "InviteContactToChatGroup")
                    {
                        InviteContactToGroupWindow inv = new InviteContactToGroupWindow(msg.Content, VmHelper.Locator.CurrentClient);
                        inv.WindowStartupLocation = System.Windows.WindowStartupLocation.CenterOwner;
                        inv.Owner = this;
                        inv.ShowDialog();
                    }
                    else if (msg.Notification == "OpenRenameGroup")
                    {
                        OpenRenameGroup(msg.Content);
                    }
                    else if (msg.Notification == "OpenManageGroup")
                    {
                        OpenManageGroup(msg.Content);
                    }
                });


            if (ProgramSettings.Instance.HasTouchInput())
            {
                RenderOptions.SetBitmapScalingMode(this, BitmapScalingMode.NearestNeighbor);//TODO: rewrite this retarded hack that makes the images render properly on tablet.
                BnOptionsMenu.Width = BnOptionsMenu.Height = 40;
                ///Turn down the framerate to 20, for less powerful tablet devices.
                Timeline.DesiredFrameRateProperty.OverrideMetadata(
                    typeof(Timeline),
                    new FrameworkPropertyMetadata
                        {
                            DefaultValue = 20
                        }
                    );
            }
            else
                RenderOptions.SetBitmapScalingMode(this, BitmapScalingMode.NearestNeighbor);
            
            //RenderOptions.SetEdgeMode(this, EdgeMode.Aliased);

        }


        private void LoadWindowState()
        {

            this.Top = Properties.Settings.Default.Top;
            this.Left = Properties.Settings.Default.Left;
            this.Height = Properties.Settings.Default.Height;
            this.Width = Properties.Settings.Default.Width;

            //restore saved window state
            if (Properties.Settings.Default.SettingStartMinimized)
                WindowState = WindowState.Minimized;
            else if (Properties.Settings.Default.Maximized)
            {
                WindowState = WindowState.Maximized;
            }
            if (!Properties.Settings.Default.SettingIsContactListVisible)
            {
                Storyboard s = Resources["ContactsControlHideAnim"] as Storyboard;
                s.Begin();
            }
        }

        private void SaveWindowState()
        {
            if (WindowState == WindowState.Maximized)
            {
                // Use the RestoreBounds as the current values will be 0, 0 and the size of the screen
                Properties.Settings.Default.Top = RestoreBounds.Top;
                Properties.Settings.Default.Left = RestoreBounds.Left;
                Properties.Settings.Default.Height = RestoreBounds.Height;
                Properties.Settings.Default.Width = RestoreBounds.Width;
                Properties.Settings.Default.Maximized = true;
            }
            else
            {
                Properties.Settings.Default.Top = this.Top;
                Properties.Settings.Default.Left = this.Left;
                Properties.Settings.Default.Height = this.Height;
                Properties.Settings.Default.Width = this.Width;
                Properties.Settings.Default.Maximized = false;
            }
            Properties.Settings.Default.Save();
        }
        private void CreateJumpList()
        {
            JumpList jumpList = new JumpList();
            JumpList.SetJumpList(Application.Current, jumpList);


            //JumpTask helloTask = new JumpTask
            //    {
            //        Title = "Say Hello",
            //        Description = "Shows a Hello World message.",
            //        ApplicationPath = Assembly.GetEntryAssembly().Location,
            //        IconResourcePath = Assembly.GetEntryAssembly().Location,
            //        IconResourceIndex = 0,
            //        Arguments = "/hello"
            //    };
            //jumpList.JumpItems.Add(helloTask);

            //JumpTask goodbyeTask = new JumpTask
            //    {
            //        Title = "Say Goodbye",
            //        Description = "Shows a goodbye message.",
            //        ApplicationPath = Assembly.GetEntryAssembly().Location,
            //        IconResourcePath = Assembly.GetEntryAssembly().Location,
            //        IconResourceIndex = 0,
            //        Arguments = "/goodbye"
            //    };
            //jumpList.JumpItems.Add(goodbyeTask);

            jumpList.Apply();
        }
        #endregion

        #region UI


        private void StartWindowDrag(Window win)
        {
            this.Dispatcher.BeginInvoke(() =>
                {
                    win.Topmost = true;
                    W32Point pt = new W32Point();
                    if (!Win32.GetCursorPos(ref pt))
                    {
                        Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
                    }
                    win.Left = pt.X - win.Width + 200;
                    win.Top = pt.Y - 20;
                    //win.Visibility = System.Windows.Visibility.Visible;

                    Debug.Print("Mouse position: {0}", pt.X);
                    Debug.Print("Window top: {0}", win.Top);

                 //   if (Mouse.LeftButton == MouseButtonState.Pressed)
                        win.DragMove();
                //    else
                 //       Debug.Print("Left mouse button is not pressed");



                    win.Topmost = false;
                    //this.Topmost = false;
                    //Task.Factory.StartNew(() =>
                    //    {


                    //        // Set is never called, so we wait always until the timeout occurs
                    //        using (EventWaitHandle tmpEvent = new ManualResetEvent(false))
                    //        {
                    //            tmpEvent.WaitOne(TimeSpan.FromMilliseconds(50));
                    //            this.Dispatcher.BeginInvoke(() => win.Topmost = false);
                    //        }
                    //    });
                       win.ReleaseMouseCapture();
                });
        }


        private void OpenResetPasswordWindow()
        {
            ResetPasswordWindow reset = new ResetPasswordWindow();
            reset.Owner = this;
            reset.WindowStartupLocation = System.Windows.WindowStartupLocation.CenterOwner;
            if (reset.ShowDialog() == true) { }
        }

        private void OpenRegisterWindow()
        {
            RegisterWindow reg = new RegisterWindow();
            reg.Owner = this;
            reg.WindowStartupLocation = WindowStartupLocation.CenterOwner;
            if (reg.ShowDialog() == true) { }
        }

        private void OpenNotifications()
        {
            NotificationsWindow nw = new NotificationsWindow();
            nw.Owner = this;
            nw.WindowStartupLocation = WindowStartupLocation.CenterOwner;
            nw.Show();
        }

        private void OpenCreateGroups()
        {
            AddGroupWindow ag = new AddGroupWindow();
            ag.WindowStartupLocation = System.Windows.WindowStartupLocation.CenterOwner;
            ag.Owner = this;
            if (ag.ShowDialog() == true) { }

        }

        private void OpenManageGroup(IChatGroup group)
        {
            var win = new ManageGroupWindow(group, VmHelper.Locator.CurrentClient);
            win.Owner = this;
            win.WindowStartupLocation = System.Windows.WindowStartupLocation.CenterScreen;
            if (win.ShowDialog() == true) { }
        }
        private void OpenRenameGroup(IChatGroup group)
        {
            var win = new RenameGroupWindow(group, VmHelper.Locator.CurrentClient);
            win.Owner = this;
            win.WindowStartupLocation = System.Windows.WindowStartupLocation.CenterScreen;
            if (win.ShowDialog() == true) { }
        }
        private void OpenAddContacts(string targetUsername)
        {
            AddContactWindow ac = new AddContactWindow(targetUsername, VmHelper.Locator.CurrentClient);
            ac.WindowStartupLocation = System.Windows.WindowStartupLocation.CenterOwner;
            ac.Owner = this;
            if (ac.ShowDialog() == true) { }
        }

        private void OpenContactListWindow()
        {
            ContactListWindow clw = new ContactListWindow();
            clw.WindowStartupLocation = System.Windows.WindowStartupLocation.CenterScreen;
            clw.Show();
        }

        private void OpenAddEmoticonWindow(Window win, IEmoticon e = null)
        {
            ViewModelMainWindow mainWindowVm = this.DataContext as ViewModelMainWindow;
            if (win == null)
                win = this;
            var locator = VmHelper.Locator;
            IClient currentClient = locator.CurrentClient;
            string savePath = StaticMethods.CombinePath(ProgramSettings.Instance.CustomEmoticonFolder, locator.CurrentClient.CurrentUserInfo.UserName);
            if (!Directory.Exists(savePath))
            {
                Directory.CreateDirectory(savePath);
            }
            var customEmoticon = e as JiveCustomEmoticonModel;
            string path = customEmoticon != null ? customEmoticon.EmoticonPath : "";
            string key = customEmoticon != null ? customEmoticon.EmoticonShortcut : "";
            var ae = new AddEmoticonWindow(path, key)
            {
                Owner = win,
                WindowStartupLocation = WindowStartupLocation.CenterOwner
            };
            if (ae.ShowDialog() == true)
            {
                FileInfo file = ae.SourcePath;
                string shortcut = ae.Shortcut;
                string hash = ae.SourcePath.GetMD5();


                var fileName = hash + file.Extension;
                savePath = StaticMethods.CombinePath(savePath, fileName);
                StaticMethods.SaveImageData(ae.ImageSource, savePath);

                FileInfo savedFile = file;
                HashSet<ContactEmoticon> emoticons = currentClient.UserSettings.CustomEmoticons;

                if (!emoticons.Any(x => x.Keyword == shortcut))
                {
                    emoticons.Add(new ContactEmoticon()
                    {
                        Keyword = shortcut,
                        Hash = hash,
                        Index = emoticons.Select(x => x.Index).OrderBy(n => n).LastOrDefault() + 1,
                        Extension = ae.SourcePath.Extension
                    });
                }
                currentClient.UserSettings.CustomEmoticons = currentClient.CurrentUserInfo.CustomEmoticons = emoticons;
                Messenger.Default.Send(new NotificationMessage(this, "UpdateCustomEmoticons"));
                PcUserSettings settings = new PcUserSettings();
                settings.CustomEmoticons = currentClient.UserSettings.CustomEmoticons;
                currentClient.Connection.SendCommand(settings, ClientCommands.PcSettings);
            }


            if (mainWindowVm.CurrentActiveViewModel is ViewModelChat)
                ((ViewModelChat)mainWindowVm.CurrentActiveViewModel).IsInputTextFocused = true;
            //Task.Factory.StartNew(() =>
            //    {
            //        Thread.Sleep(250);
            //        //We run a scroll event on the chat control after a small delay. If we do not do this, the page will scroll when the emoticons render,
            //        //and the auto scroll on new message will stop functioning
            //        App.Current.Dispatcher.BeginInvoke(() =>
            //            {
            //                Messenger.Default.Send(new NotificationMessage(mainWindowVm.CurrentActiveViewModel, "ForceScrollDocument"));
            //            });
            //    });
        }

        private void EditEmoticon(Window win, IEmoticon e)
        {
            if (e == null)
                return;
            if (win == null)
                win = this;
            switch (e.Type)
            {
                case EmoticonType.CustomEmoticon:
                    {
                        var customEmoticon = (JiveCustomEmoticonModel)e;
                        string shortcut = (string)customEmoticon.EmoticonShortcut;
                        string path = customEmoticon.EmoticonPath;
                        if (string.IsNullOrEmpty(path))
                            return;
                        IClient currentClient = VmHelper.Locator.CurrentClient;
                        AddEmoticonWindow ae = new AddEmoticonWindow(path, shortcut);
                        ae.WindowStartupLocation = WindowStartupLocation.CenterOwner;
                        ae.Owner = win;// Window.GetWindow(this);
                        ContactEmoticon ce = currentClient.UserSettings.CustomEmoticons.FirstOrDefault(x => x.Keyword == shortcut);
                        if (ae.ShowDialog() == true)
                        {
                            if (ae.SourcePath == null || String.IsNullOrEmpty(ae.Shortcut))
                                return;

                            customEmoticon.EmoticonPath = ae.SourcePath.FullName;
                            customEmoticon.EmoticonShortcut = ae.Shortcut;

                            string hash = ae.SourcePath.GetMD5();

                            if (ce != null)
                                currentClient.UserSettings.CustomEmoticons.Remove(ce);
                            currentClient.UserSettings.CustomEmoticons.Add(new ContactEmoticon()
                            {
                                Keyword = ae.Shortcut,
                                Hash = hash,
                                Index = ce.Index,
                                Extension = ae.SourcePath.Extension
                            });
                            PcUserSettings settings = new PcUserSettings();
                            settings.CustomEmoticons = currentClient.UserSettings.CustomEmoticons;
                            currentClient.CurrentUserInfo.CustomEmoticons = settings.CustomEmoticons;
                            currentClient.Connection.SendCommand(settings, ClientCommands.PcSettings);
                            Messenger.Default.Send(new NotificationMessage(this, "UpdateCustomEmoticons"));
                            Task.Factory.StartNew(() =>
                            {
                                // Set is never called, so we wait always until the timeout occurs
                                using (EventWaitHandle tmpEvent = new ManualResetEvent(false))
                                {
                                    tmpEvent.WaitOne(TimeSpan.FromMilliseconds(250));
                                }

                                //We run a scroll event on the chat control after a small delay. If we do not do this, the page will scroll when the emoticons render,
                                //and the auto scroll on new message will stop functioning
                                App.Current.Dispatcher.BeginInvoke(() =>
                                {
                                    Messenger.Default.Send(new NotificationMessage(((ViewModelMainWindow)this.DataContext).CurrentActiveViewModel, "ForceScrollDocument"));
                                });
                            });
                        }
                        break;
                    }
            }
        }

        private ChatWindow GetChatWindow(string id)
        {
            //IChatWindow vmp;
            ChatWindow window = null;
            //if (VmHelper.Locator.CurrentClient.OpenWindowViewModels.TryGetValue(id, out vmp))
            //{
            Messenger.Default.Send(new NotificationMessageAction<ChatWindow>(this, id, "GetChatWindowByID", reply =>
            {
                window = reply;
            }));
            //}
            return window;
        }

        private ChatWindow OpenChatWindow(ViewModelChat vm, bool openAtMouse = false)
        {
            ChatWindow chatWindow = GetChatWindow(vm.ID);
            if (chatWindow != null)
            {
                if (chatWindow.WindowState == WindowState.Minimized)
                {
                    chatWindow.WindowState = WindowState.Normal;
                }
                chatWindow.Topmost = false;
                chatWindow.IsFlashing = true;
                chatWindow.SetActive();
            }
            else
            {

                chatWindow = new ChatWindow(vm);


                chatWindow.WindowStartupLocation = System.Windows.WindowStartupLocation.CenterScreen;
                chatWindow.LocationChanged += ChatWindow_LocationChanged;
                chatWindow.Closing += ChatWindow_Closing;
                if (openAtMouse)
                {
                    chatWindow.WindowStartupLocation = System.Windows.WindowStartupLocation.Manual;
                    W32Point pt = new W32Point();
                    if (!Win32.GetCursorPos(ref pt))
                    {
                        Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
                    }
                    chatWindow.Left = pt.X - chatWindow.Width + 200;
                    chatWindow.Top = pt.Y - 20;
                }
                chatWindow.Show();

            }
            return chatWindow;
        }


        private Window FindWindowUnderThisAt(Window source, Point screenPoint)  // WPF units (96dpi), not device units
        {
            return (
              from win in SortWindowsTopToBottom(Application.Current.Windows.OfType<Window>())
              where new Rect(win.Left, win.Top, win.Width, win.Height).Contains(screenPoint)
              && !Equals(win, source)
              select win
            ).FirstOrDefault();
        }
        private IEnumerable<Window> SortWindowsTopToBottom(IEnumerable<Window> unsorted)
        {
            var byHandle = unsorted.ToDictionary(win =>
                ((HwndSource)PresentationSource.FromVisual(win)).Handle);

            foreach (IntPtr hWnd in Win32.GetAllWindows())
            {
                if (byHandle.ContainsKey(hWnd))
                    yield return byHandle[hWnd];
            }
        }
        private void OpenLog(Window owner = null)
        {
            if (owner == null)
                owner = this;
            string path = StaticMethods.CombinePath(ProgramSettings.Instance.LogFolder, VmHelper.Locator.CurrentClient.CurrentUserInfo.UserName);
            LogsWindow ol = new LogsWindow(path);

            ol.Owner = owner;
            ol.WindowStartupLocation = System.Windows.WindowStartupLocation.CenterOwner;
            ol.ShowDialog();
        }

        private void EditFont(Window owner = null)
        {
            if (owner == null)
                owner = this;

            IClient currentClient = VmHelper.Locator.CurrentClient;

            var fontFamily = StaticMethods.ConvertFromString<FontFamily>(currentClient.UserSettings.FontFamily);
            var fontStyle = StaticMethods.ConvertFromString<FontStyle>(currentClient.UserSettings.FontStyle);
            var fontWeight = StaticMethods.ConvertFromString<FontWeight>(currentClient.UserSettings.FontWeight);
            var fontSize = currentClient.UserSettings.FontSize.GetValueOrDefault();
            var textForegroundColor = StaticMethods.HexStringToFrozenBrush(currentClient.UserSettings.FontHexColor);
            var dialog = new ColorFontDialog
            {

                Font = new FontInfo(fontFamily, fontSize,
                     fontStyle, FontStretches.Normal, fontWeight, textForegroundColor),
            };

            dialog.Owner = owner;
            dialog.WindowStartupLocation = System.Windows.WindowStartupLocation.CenterOwner;
            if (dialog.ShowDialog().GetValueOrDefault() == true)
            {
                FontInfo font = dialog.Font;
                //Send the updated font information to the server for storage
                currentClient.UserSettings.FontFamily = currentClient.CurrentUserInfo.FontFamily = StaticMethods.ConvertToString(font.Family);
                currentClient.UserSettings.FontStyle = currentClient.CurrentUserInfo.FontStyle = StaticMethods.ConvertToString(font.Style);
                currentClient.UserSettings.FontWeight = currentClient.CurrentUserInfo.FontWeight = StaticMethods.ConvertToString(font.Weight);
                currentClient.UserSettings.FontSize = font.Size;
                currentClient.UserSettings.FontHexColor = currentClient.CurrentUserInfo.Color = StaticMethods.ColorToHexString(font.BrushColor.Color);
                currentClient.Connection.SendCommand(currentClient.UserSettings, ClientCommands.PcSettings);
                Messenger.Default.Send(new NotificationMessage(this, "UpdateFontSize"));
            }
        }

        private void AddUserEmoticons()
        {
            GetUserEmoticons aue = new GetUserEmoticons()
            {
                WindowStartupLocation = WindowStartupLocation.CenterOwner,
                Owner = this
            };
            aue.ShowDialog();
            Task.Factory.StartNew(() =>
            {


                // Set is never called, so we wait always until the timeout occurs
                using (EventWaitHandle tmpEvent = new ManualResetEvent(false))
                {
                    tmpEvent.WaitOne(TimeSpan.FromMilliseconds(250));
                }


                //We run a scroll event on the chat control after a small delay. If we do not do this, the page will scroll when the emoticons render,
                //and the auto scroll on new message will stop functioning
                App.Current.Dispatcher.BeginInvoke(() =>
                {
                    Messenger.Default.Send(new NotificationMessage(((ViewModelMainWindow)this.DataContext).CurrentActiveViewModel, "ForceScrollDocument"));
                });
            });


        }

        private bool OpenOptions(Window owner = null, OptionsPage page = OptionsPage.Personal)
        {
            if (owner == null)
                owner = this;
            var locator = VmHelper.Locator;
            IClient currentClient = locator.CurrentClient;
            JmOptions jmo = new JmOptions(locator.CurrentClient, page);
            jmo.Owner = owner;
            jmo.WindowStartupLocation = WindowStartupLocation.CenterOwner;

            if (jmo.ShowDialog() == true)
            {
                foreach (var vm in ((ViewModelMainWindow)this.DataContext).ChatViewModels.Where(x => x is ViewModelChat).Cast<ViewModelChat>())
                {
                    vm.IsNudgeButtonVisible = currentClient.UserSettings.EnableNudge.GetValueOrDefault();
                    if (vm is ViewModelPrivateMessage)
                        vm.ShowAsTab = currentClient.PrivateMessageBehavior != PrivateMessageBehavior.Windows;
                }
                foreach (ViewModelPrivateMessageWindow vm in currentClient.OpenWindowViewModels.Values)
                {
                    vm.CurrentActiveViewModel.IsNudgeButtonVisible = currentClient.UserSettings.EnableNudge.GetValueOrDefault();
                }
                Messenger.Default.Send(new NotificationMessage(this, "InitKeyBindings"));
                var windowTitle = string.Empty;
                if (!String.IsNullOrEmpty(currentClient.CurrentUserInfo.NickName))
                    windowTitle = "Jive Messenger" + " - " + currentClient.CurrentUserInfo.NickName + " <" + currentClient.CurrentUserInfo.UserName + ">";
                else
                    windowTitle = "Jive Messenger" + " - " + currentClient.CurrentUserInfo.UserName;
                ((ViewModelMainWindow)this.DataContext).WindowTitle = windowTitle;

                if (currentClient.UserSettings.EnableSpotifyNowPlaying.GetValueOrDefault() == true && currentClient.SpotifyApi == null)
                    currentClient.InitSpotifyApi();


                currentClient.Connection.SendCommand(currentClient.UserSettings, ClientCommands.PcSettings);

                ICollectionView view = CollectionViewSource.GetDefaultView(((ViewModelMainWindow)this.DataContext).ChatViewModels) as ICollectionView;
                view.Refresh();
                return true;
            }
            return false;
        }


        /// <summary>
        /// Shows the message.
        /// </summary>
        /// <param name="message">The message.</param>
        private void ShowMessage(string message)
        {
            this.Dispatcher.BeginInvoke(() => MessageBox.Show(message));
        }

        #endregion
        #region overrides
        protected override void OnClosing(CancelEventArgs e)
        {
            ((ViewModelMainWindow)this.DataContext).LogoutCommand.Execute(true);
            SaveWindowState();

            // Set is never called, so we wait always until the timeout occurs
            using (EventWaitHandle tmpEvent = new ManualResetEvent(false))
            {
                tmpEvent.WaitOne(TimeSpan.FromMilliseconds(25));
            }

            base.OnClosing(e);
        }

        protected override void OnClosed(EventArgs e)
        {
            base.OnClosed(e);
            // Environment.Exit(0);
        }
        protected override void OnActivated(EventArgs e)
        {
            // Application activated
            if (IsFlashing)
            {
                this.StopFlashingWindow();
                IsFlashing = false;
            }
            base.OnActivated(e);
        }

        protected override void OnDeactivated(EventArgs e)
        {
            //  Messenger.Default.Send(new NotificationMessage(this, "StopImagePopup"));
            //  Messenger.Default.Send(new NotificationMessage(this, "HideImagePreview"));
            base.OnDeactivated(e);
        }
        protected override void OnStateChanged(EventArgs e)
        {
            if (IsFlashing)
            {
                this.StopFlashingWindow();
                IsFlashing = false;
            }
            base.OnStateChanged(e);
        }
        #endregion
        #region events
        void ChatWindow_Closing(object sender, CancelEventArgs e)
        {
            ChatWindow win = (ChatWindow)sender;

            win.Closing -= ChatWindow_Closing;
            win.LocationChanged -= ChatWindow_LocationChanged;

        }
        void ChatWindow_LocationChanged(object sender, EventArgs e)
        {
            Window win = (Window)sender;
            if (!win.IsLoaded)
                return;
            W32Point pt = new W32Point();
            if (!Win32.GetCursorPos(ref pt))
            {
                Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
            }
            Point p = new Point(pt.X, pt.Y);

            var absoluteScreenPos = p;
            var windowUnder = FindWindowUnderThisAt(win, absoluteScreenPos);

            if (windowUnder != null && windowUnder.Equals(this))
            {
                Point relativePoint = this.PointFromScreen(p);

                FrameworkElement element = this.ChromeTabControl.InputHitTest(relativePoint) as FrameworkElement;//Hit test against the tab control
                if (element != null)
                {
                    ChromeTabItem tabItem = element.TemplatedParent as ChromeTabItem;
                    Point insideTabBarPoint = this.ChromeTabControl.PointFromScreen(p);
                    if ((element is ChromeTabPanel || tabItem != null) && insideTabBarPoint.X < (this.ChromeTabControl.ActualWidth - this.BnOptionsMenu.ActualWidth - 30))
                    {
                        if (win is ChatWindow)
                        {
                            ViewModelChatWindow chatWindowVm = (ViewModelChatWindow)win.DataContext;
                            ViewModelMainWindow mainWindowVm = (ViewModelMainWindow)this.DataContext;

                            if (mainWindowVm.ChatViewModels.Contains(chatWindowVm.CurrentActiveViewModel))
                                chatWindowVm.CurrentActiveViewModel.ShowAsTab = true;
                            else
                                mainWindowVm.ChatViewModels.Add(chatWindowVm.CurrentActiveViewModel);
                            win.Close();

                            mainWindowVm.CurrentActiveViewModel = chatWindowVm.CurrentActiveViewModel;
                            this.ChromeTabControl.GrabTab(chatWindowVm.CurrentActiveViewModel);
                        }
                    }
                }
            }
        }

        private void Close_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            Application.Current.Shutdown();
        }

        private void BnOptionsMenu_ContextMenuOpening(object sender, ContextMenuEventArgs e)
        {
            if (Mouse.RightButton == MouseButtonState.Pressed)
            {
                Button bn = sender as Button;
                if (bn != null) bn.ContextMenu.Visibility = Visibility.Hidden;
            }
            e.Handled = true;
        }

        private void HandleUnhandledExceptions(object sender, UnhandledExceptionEventArgs args)
        {
            this.Dispatcher.Invoke(() =>
                {
                    Exception e = (Exception)args.ExceptionObject;
                    string error = "Unhandled exception: " + e.Message;
                    while (e.InnerException != null)
                    {
                        e = e.InnerException;
                        error += " : Inner Exception = " + e.Message;
                    }
                    Logger.ErrorException("Unhandled exception caught: " + error, e);

                    foreach (var vm in ((ViewModelMainWindow)this.DataContext).ChatViewModels.Where(x => x is ViewModelChat).Cast<ViewModelChat>())
                    {
                        vm.WriteLog();
                    }
                });
        }

        private void JiveMainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            adornerLayer = AdornerLayer.GetAdornerLayer(Overlay);
            adornerLayer.IsEnabled = Properties.Settings.Default.SettingIsContactListVisible;
            foreach (Panel toAdorn in Overlay.Children)
            {
                adornerLayer.Add(new ResizingAdorner(BottomSnapBorder, toAdorn.Children[0]));
                adornerLayer.Add(new ResizingAdorner(TopSnapBorder, toAdorn.Children[0]));
            }
        }

        private void MainMenu_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            ((Menu)sender).UpdateLayout();
            if ((bool)e.NewValue == true)
                ((MenuItem)((Menu)sender).Items[0]).Focus();
        }




        private void ContactVisible_Changed(object sender, EventArgs e)
        {
            if (Overlay == null)
                return;

            adornerLayer = AdornerLayer.GetAdornerLayer(Overlay);
            adornerLayer.IsEnabled = Properties.Settings.Default.SettingIsContactListVisible;
        }
        private void ShakeWindowAnim_Completed(object sender, EventArgs e)
        {
            Storyboard board = this.Resources["ShakeWindowAnim"] as Storyboard;
            board.Remove();
        }

        private void ChromeTabControl_TabDraggedOutsideBonds(object sender, TabDragEventArgs e)
        {
            if (e.Tab is ViewModelChat)
            {
                // e.Handled = true;
                ViewModelChat tab = e.Tab as ViewModelChat;
                if (tab.IsPinned)
                    return;
                ChatWindow win = OpenChatWindow(tab, true);

                win.DoWhenLoaded(chatWin =>
                    {
                        StartWindowDrag(chatWin);
                        // win.Visibility = System.Windows.Visibility.Visible;
                    });
            }
        }
        #endregion
    }
}